package util;
 
/**
 * 使用3线程等待方法对外部Console程序进行操作的封装功能类
 * 本类为主要使用类
 * 
 * @author BKMMSC 金鸡独立
 * @version 1.0
 * @since 2014
 *
 */
 
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.PrintStream;
 
public class ExternalProgramUtil {
 
    /**
     * runtime对象，用于执行exec方法并返回process对象
     */
    public Runtime runtime=Runtime.getRuntime();
    /**
     * 存放并与其他子线程共享用于操作外部程序的process对象
     */
    public Process process;
    /**
     * 请注意此字段！此字段将会更新并反馈目标程序的标准错误信息
     */
    public String errorinfo;
    /**
     * 请注意此字段！此字段将会更新并反馈目标程序的标准输出信息
     */
    public String outputinfo;
    /**
     * 用于随时监视目标程序标准错误流的子进程的实例，在本类实例化时被创建
     */
    public ErrorStream es;
    /**
     * 用于随时监视目标程序标准输出流的子进程的实例，在本类实例化时被创建
     */
    public OutputStream os;
    /**
     * 对目标程序进行标准输入操作的流对象，每次使用其进行手动输入时需要ps.flush()
     */
    public PrintStream ps;
 
    /**
     * 直接执行命令，返回不加处理的process，如果要使用该类，请实例化
     * 
     * @param command 执行的命令，cmd格式
     * @return 未经处理的process对象
     * @throws IOException 当指令错误或执行错误时
     */
    public static Process pexe(String command) throws IOException{
        return Runtime.getRuntime().exec(command);
    }
 
    /**
     * 构造方法之一，只有一个启动外部程序的文本，相当于Runtime.exec(exec);，但会对子进程和process进行初始化
     * 
     * @param exec 执行的命令，cmd格式
     * @throws IOException 当指令错误或执行错误时
     */
    public ExternalProgramUtil(String exec) throws IOException{
        process=runtime.exec(exec);
        es=new ErrorStream();
        os=new OutputStream();
        es.process=process;
        os.process=process;
        es.start();
        os.start();
        ps=new PrintStream(new BufferedOutputStream(process.getOutputStream()));
    }
 
    /**
     * 得到目标程序的标准错误流线程
     * 
     * @return 返回目前的标准错误流线程对象
     */
    public ErrorStream getEs() {
        return es;
    }
 
    /**
     * 指定目标程序的标准错误流线程
     * 
     * @param es 要设置的标准错误流线程对象
     */
    public void setEs(ErrorStream es) {
        this.es = es;
    }
 
    /**
     * 得到目标程序的标准输出流线程
     * 
     * @return 返回目前的标准输出流线程对象
     */
    public OutputStream getOs() {
        return os;
    }
 
    /**
     * 指定目标程序的标准输出流线程
     * 
     * @param os 要设置的标准输出流线程对象
     */
    public void setOs(OutputStream os) {
        this.os = os;
    }
 
    /**
     * 向目标程序输入指定信息，使用本方法无需flush，本方法不自带换行
     * 
     * @param arg 要输入到目标成的的文本，请他类型请进行转换
     */
 
    public void print(String arg){
        ps.print(arg);
        ps.flush();
    }
 
    /**
     * 得到目标程序的标准错误流信息，调用此getter则不需要调用更新方法
     * 
     * @return 返回目前的标准错误流信息
     */
    public String getErrorinfo() {
        this.updateerr();
        return errorinfo;
    }
 
    /**
     * 得到目标程序的标准输出流信息，调用此getter则不需要调用更新方法
     * 
     * @return 返回目前的标准输出流信息
     */
    public String getOutputinfo() {
        this.updateout();
        return outputinfo;
    }
 
    /**
     * 向目标程序输入一个换行，使用本方法无需flush，本方法自带换行
     * 
     */
    public void println(){
        ps.println();
        ps.flush();
    }
 
    /**
     * 向目标程序输入指定信息，使用本方法无需flush，本方法自带换行
     * 
     * @param arg 要输入到目标成的的文本，请他类型请进行转换
     */
    public void println(String arg){
        ps.println(arg);
        ps.flush();
    }
 
    /**
     * 对目标程序的标准错误流信息进行更新，使用对应的getter方法则不需要调用本方法
     * 
     */
    public void updateerr(){
        errorinfo=es.err;
    }
 
    /**
     * 对目标程序的标准输出流信息进行更新，使用对应的getter方法则不需要调用本方法
     * 
     */
    public void updateout(){
        outputinfo=os.out;
    }
 
    /**
     * 已过时！不建议使用！
     * 关闭已经连接的3流，停止2个子线程，释放资源而不是等待它们被Java自行回收
     * 不推荐使用，由于关闭线程存在死锁的危险性，具体原因可搜索：为什么不建议使用Thread.stop()方法
     * 如果希望更安全地释放资源，可以使用本类的gc()方法启动Java垃圾回收器回收资源
     * 使用前请先进行延时等待
     * 
     * @deprecated
     */
    public void close(){
        es.stop();
        os.stop();
        ps.close();
        runtime=null;
        process=null;
        errorinfo=null;
        outputinfo=null;
        es=null;
        os=null;
        ps=null;
    }
 
    /**
     * 启动Java自动垃圾回收器，更安全地关闭已经连接的3流，停止2个子线程，释放资源而不是等待它们被Java自行回收
     * 请尽量调用本方法而不要使用stop()方法，这样可以避免死锁等问题并更安全地释放资源
     * 使用前请先进行延时等待
     * 
     */
    public void gc(){
        es.interrupt();
        os.interrupt();
        runtime=null;
        process=null;
        errorinfo=null;
        outputinfo=null;
        es=null;
        os=null;
        ps=null;
    }
}